#include <jni.h>
#include <string>
#include <iostream>
#include <vector>
#include <android/log.h>

extern "C" {
#include "include/libavformat/avformat.h"
#include "include/libavcodec/avcodec.h"
}

#define LOG_TAG "ffmpeg"

using namespace std;

int ts2Mp4(const string,const string,int);


/*extern "C" JNIEXPORT jstring JNICALL
Java_com_example_teskndk_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject *//* this *//*) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}*/

extern "C" JNIEXPORT jint JNICALL
Java_com_songcha_sakura_util_FFMpegUtil_ts2Mp4(
        JNIEnv* env,
        jclass jcls,
        jstring j_ts_path,
        jstring j_mp4_path,
        jint ts_count
) {

    string ts_path=env->GetStringUTFChars(j_ts_path, nullptr);
    string mp4_path=env->GetStringUTFChars(j_mp4_path,nullptr);

    //__android_log_print(ANDROID_LOG_INFO,LOG_TAG, "%s,%s\n",ts_path.c_str(),mp4_path.c_str());

    int ret=ts2Mp4(ts_path,mp4_path,ts_count);

    return ret;
}



int ts2Mp4(const string ts_path,const string mp4_path,int ts_count){



    // create output context
    AVFormatContext* output_ctx = NULL;
    if (avformat_alloc_output_context2(&output_ctx, NULL, NULL, mp4_path.c_str()) < 0) {
        __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Failed to create output context\n");
        return -1;
    }

    //set the max streams number,default is 1000
    output_ctx->max_streams=3000;

    int video_stream_idx = -1;

    vector<AVFormatContext*> input_ctx_list;

    for(int i=0;i<ts_count;i++){
        // open input file
        char ts[256];
        sprintf(ts,"%s%s%d%s",ts_path.c_str(),"/",i,".ts");
        //string ts=tmp;
        //log("%s\n",ts);
        //__android_log_print(ANDROID_LOG_INFO,LOG_TAG, "%s\n",ts);

        if(strcmp(".ts",strstr(ts,".ts"))!=0) continue;


        AVFormatContext* input_ctx = NULL;
        if (avformat_open_input(&input_ctx, ts, NULL, NULL) != 0) {
            //fprintf(stderr, "Failed to open input file '%s'\n", ts);
            __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Failed to open input file '%s'\n",ts);
            return -1;
        }
        if (avformat_find_stream_info(input_ctx, NULL) < 0) {
            //fprintf(stderr, "Failed to retrieve input stream information\n");
            __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Failed to retrieve input stream information\n");
            return -1;
        }

        input_ctx_list.push_back(input_ctx);

        // add streams
        for (int i = 0; i < input_ctx->nb_streams; i++) {
            AVStream* in_stream = input_ctx->streams[i];
            AVCodecParameters* in_codecpar = in_stream->codecpar;

            if (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO || in_codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
                AVStream* out_stream = avformat_new_stream(output_ctx, NULL);
                if (!out_stream) {
                    //fprintf(stderr, "Failed to allocate output stream\n");
                    __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Failed to allocate output stream\n");
                    return -1;
                }
                if (avcodec_parameters_copy(out_stream->codecpar, in_codecpar) < 0) {
                    //fprintf(stderr, "Failed to copy codec parameters\n");
                    __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Failed to copy codec parameters\n");
                    return -1;
                }

                out_stream->codecpar->codec_tag = 0;

                if (in_codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
                    video_stream_idx = out_stream->index;
            }
        }

    }


    // open output file
    if (!(output_ctx->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&output_ctx->pb, mp4_path.c_str(), AVIO_FLAG_WRITE) < 0) {
            // fprintf(stderr, "Could not open output file '%s'\n", mp4_path.c_str());
            __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Could not open output file '%s'\n", mp4_path.c_str());
            return -1;
        }
    }
    // write header
    if (avformat_write_header(output_ctx, NULL) < 0) {
        // fprintf(stderr, "Error occurred when opening output file\n");
        __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Error occurred when opening output file\n");
        return -1;
    }

    int pkt_cnt = 0;
    // copy packets

    //int stream_index = 0;

    for(int i=0;i<input_ctx_list.size();i++){
        AVFormatContext* input_ctx=input_ctx_list[i];
        AVPacket packet;
        int ret = 0;


        while (av_read_frame(input_ctx, &packet) >= 0) {
            //printf("%d,%d\n",video_stream_idx,packet.stream_index);
            AVStream* in_stream = input_ctx->streams[packet.stream_index];
            AVStream* out_stream = output_ctx->streams[packet.stream_index];

            // copy packet
            packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base,
                                          (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base,
                                          (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
            packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base);

            //printf("%ld,%ld,%ld\n",packet.pts,packet.dts,packet.duration);
            // __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Error occurred when opening output file\n");

            packet.pos = -1;
            if (packet.stream_index == video_stream_idx) {
                //printf("Send video %8d\n", pkt_cnt);
                pkt_cnt++;
            }

            ret = av_interleaved_write_frame(output_ctx, &packet);
            if (ret < 0) {
                //fprintf(stderr, "Error muxing packet\n");
                __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Error muxing packet\n");
                break;
            }
            av_packet_unref(&packet);
        }
        // close input
        avformat_close_input(&input_ctx);
    }


    // write trailer
    if (av_write_trailer(output_ctx) < 0) {
        //fprintf(stderr, "Error occurred when writing trailer\n");
        __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "Error occurred when writing trailer\n");
        return -1;
    }

    // close output
    if (output_ctx && !(output_ctx->oformat->flags & AVFMT_NOFILE))
        avio_closep(&output_ctx->pb);
    avformat_free_context(output_ctx);

    //printf("convert success!\n");
    __android_log_print(ANDROID_LOG_INFO,LOG_TAG, "convert success!\n");


    return 0;
}
